#include "bitio.h"
#include "dec_table.h"

char lookup(index_t current,node *table,FILE* fd);

int lzdecompress(char* file_name){
	
	uint32_t d;
	int ret;
	char c; 

	BITFILE *bitbuff;
	node *table;
	
	index_t prev_node=-1, current_node = -1;
	int ultimo_inserito = ROOT_INDEX+1;
	int bit_da_leggere = 9;
	
	
	/***gestione nome file decompresso(elimino l'estensione .gc)***/
	char file_dest[255];
	char *ptr = NULL;  	
	char *app = calloc(249,1);
	if (calloc==NULL){
		fprintf(stderr, "Errore nell'allocazione della memoria!\n");
		return -1;
	}
	ptr = strrchr(file_name, '.'); 
  	if (ptr != NULL) 
  		snprintf(app, (ptr-file_name)+1, "%s", file_name);
	strcpy(file_dest,"(dec)_");
	strcat (file_dest,app);
	free(app);	

	/*** apertura file in cui verranno scritti i dati decompressi***/
	FILE* fd = fopen(file_dest,"w");
	if(fd == NULL){
		fprintf(stderr,"Impossibile aprire il file %s. Chiusura in corso...\n", file_dest);
		return -1;	
	}
	
	/*** allocazione tabella di decompressione e inizializzazione ***/
	table = calloc(M_ENTRY,sizeof(node));
	if (table==NULL){
		fprintf(stderr, "Errore nell'allocazione della memoria!\n");
		fclose(fd);	
		return -1;
	}
	init_table(table);	
		
	/*** apertura file compresso e inizializzazione struttura bitbuff***/
	bitbuff = bit_open(file_name, 0);
	if (bitbuff == NULL){
		fprintf(stderr,"Impossibile allocare un nuovo elemento bitbuff. Chiusura in corso...\n");
		fclose(fd);
		free(table);		
		return -1;
	}
		
	/***riempimento buffer per la prima lettura***/
	if(fill_buffer(bitbuff) == -1 ){
		fprintf(stderr, "Si è verificato un errore nella gestione del BitBuffer. Chiusura in corso...\n");
		fclose(fd);
		free(table);
		bit_close(bitbuff);		
		return -1;
	}
	
	/***inizio decompressione***/
	while(1){	
		/***leggo un indice dal file compresso***/
		if (ultimo_inserito == M_ENTRY-1)
			bit_da_leggere = 9;
		else
			bit_da_leggere = ceil((log(ultimo_inserito+1)/LOG2));
				
		ret = bit_read_opt(bitbuff, &d, bit_da_leggere);
		
		if (ret < bit_da_leggere) {
			fprintf(stderr, "Si è verificato un errore nella lettura del file.\n");
			free(table);
			fclose(fd);
			bit_close(bitbuff);
			return -1;
		}
		/***termino quando leggo ROOT_INDEX***/
		if(d == ROOT_INDEX)
			break;
		
#ifdef VERBOSE 	
		printf("%lu:\t<%d>\t",caratteri_letti, d);
#endif

		current_node = d;	
		/***caso particolare in cui l'indice letto è il prossimo indice che avrei inserito al passo corrente***/
		if (ultimo_inserito==d){
			c = lookup(prev_node,table,fd);
			fputc(c,fd);
		}
		else	
			c = lookup(current_node,table,fd);
		
#ifdef VERBOSE 	
		printf("\n");
#endif	
		
		/***inserisco a prev_node un nuovo figlio con il valore di current***/
		if(prev_node != -1) { 
				
			insert(table, prev_node, c, ultimo_inserito);			
			ultimo_inserito ++;
			
		}	
		/***prev_node diventa il prossimo nodo in cui inserire***/
		prev_node = d;			
		
		if (ultimo_inserito == M_ENTRY) {
				
				//printf("======================\n");
				ultimo_inserito = ROOT_INDEX+1;
				current_node = -1;
							
		}

	}
	
	free(table);
	fclose(fd);
	ret = bit_close(bitbuff);
	if (ret == -1) {
		fprintf(stderr,"Si sono verificati degli errori nella chiusura del file.\n");
		return -1;	
	}
	return 0;
	
}

/*!
\fn char lookup(index_t current,node *table,FILE* fd)
\brief funzione ricorsiva per percorrere l'albero
\param current nodo corrente
\param table puntatore alla tabella
\param fd file descriptor del file che scrivo in decompressione
\return il simbolo letto dal nodo corrente
*/
char lookup(index_t current,node *table,FILE* fd){

	char c;
	/***se sono arrivato alla radice scrivo sul file il simbolo contenuto nel nodo corrente e lo restituisco (mi servirà per aggiungerlo all'albero***/
	if(table[current].parent == ROOT_INDEX){
			
			
#ifdef VERBOSE 		
			printf("%c",table[current].symbol);
#endif
			fputc(table[current].symbol,fd);
			return table[current].symbol;	
	}
	/***percorro l'albero***/
	c = lookup(table[current].parent,table,fd);
#ifdef VERBOSE		
			printf("%c",table[current].symbol);
#endif
	/***quando torno indietro nelle chiamate ricorsive scrivo il carattere restituito su file e lo ritorno per aggiungerlo all'albero***/
	fputc(table[current].symbol,fd);
	return c;
}


